/*
 * Copyright 1999-2101 Alibaba Group.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.fastjson.parser;

import static com.alibaba.fastjson.parser.JSONScanner.EOI;

import java.util.Map;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;

/**
 * 
 * @author wenshao<szujobs@hotmail.com>
 * 
 */
public class DefaultJSONParser extends AbstractJSONParser implements JSONParser {
	protected final JSONScanner lexer;
	protected final String input;
	protected final SymbolTable symbolTable;

	public DefaultJSONParser(String input) {
		this(input, EmptySymbolTable.instance, JSON.DEFAULT_PARSER_FEATURE);
	}

	public DefaultJSONParser(final String input, final SymbolTable symbolTable) {
		this(input, new JSONScanner(input, JSON.DEFAULT_PARSER_FEATURE), symbolTable);
	}

	public DefaultJSONParser(final String input, final SymbolTable symbolTable, int features) {
		this(input, new JSONScanner(input, features), symbolTable);
	}

	public DefaultJSONParser(final String input, final JSONScanner lexer, final SymbolTable symbolTable) {
		this.input = input;
		this.lexer = lexer;
		this.symbolTable = symbolTable;

		lexer.nextToken(); // prime the pump
	}

	public JSONScanner getLexer() {
		return lexer;
	}

	public String getInput() {
		return input;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public final void parseObject(final Map object) {
		if (lexer.token() != JSONToken.LBRACE) {
			throw new JSONException("syntax error, expect {, actual " + lexer.token());
		}

		for (;;) {
			lexer.skipWhitespace();
			char ch = lexer.getCurrent();
			if (isEnabled(Feature.AllowArbitraryCommas)) {
				while (ch == ',') {
					lexer.incrementBufferPosition();
					lexer.skipWhitespace();
					ch = lexer.getCurrent();
				}
			}

			String key;
			if (ch == '"') {
				key = lexer.scanSymbol(symbolTable, '"');
				lexer.skipWhitespace();
				ch = lexer.getCurrent();
				if (ch != ':') {
					throw new JSONException("expect ':' at " + lexer.pos());
				}
			} else if (ch == '}') {
				lexer.incrementBufferPosition();
				lexer.resetStringPosition();
				lexer.nextToken();
				return;
			} else if (ch == '\'') {
				if (!isEnabled(Feature.AllowSingleQuotes)) {
					throw new JSONException("syntax error");
				}

				key = lexer.scanSymbol(symbolTable, '\'');
				lexer.skipWhitespace();
				ch = lexer.getCurrent();
				if (ch != ':') {
					throw new JSONException("expect ':' at " + lexer.pos());
				}
			} else if (ch == EOI) {
				throw new JSONException("syntax error");
			} else if (ch == ',') {
				if (!isEnabled(Feature.AllowArbitraryCommas)) {
					throw new JSONException("syntax error");
				}
				lexer.incrementBufferPosition();
				continue;
			} else {
				if (!isEnabled(Feature.AllowUnQuotedFieldNames)) {
					throw new JSONException("syntax error");
				}

				key = lexer.scanSymbolUnQuoted(symbolTable);
				lexer.skipWhitespace();
				ch = lexer.getCurrent();
				if (ch != ':') {
					throw new JSONException("expect ':' at " + lexer.pos() + ", actual " + ch);
				}
			}

			lexer.incrementBufferPosition();
			lexer.skipWhitespace();
			ch = lexer.getCurrent();

			lexer.resetStringPosition();

			Object value;
			if (ch == '"') {
				lexer.scanString();
				value = lexer.stringVal();
				object.put(key, value);
			} else if (ch >= '0' && ch <= '9' || ch == '-') {
				if (lexer.isEnabled(Feature.AllowISO8601DateFormat) && lexer.scanISO8601DateIfMatch()) {
					value = lexer.getCalendar().getTime();
				} else {
					lexer.scanNumber();
					if (lexer.token() == JSONToken.LITERAL_INT) {
						value = lexer.integerValue();
					} else {
						value = lexer.decimalValue();
					}
				}
				object.put(key, value);
			} else {
				lexer.nextToken();
				value = parse();
				object.put(key, value);

				if (lexer.token() == JSONToken.RBRACE) {
					lexer.nextToken();
					return;
				} else if (lexer.token() == JSONToken.COMMA) {
					continue;
				} else {
					throw new JSONException("syntax error");
				}
			}

			lexer.skipWhitespace();
			ch = lexer.getCurrent();
			if (ch == ',') {
				lexer.incrementBufferPosition();
				continue;
			} else if (ch == '}') {
				lexer.incrementBufferPosition();
				lexer.resetStringPosition();
				lexer.nextToken();
				return;
			} else {
				throw new JSONException("syntax error");
			}

		}
	}


}
